{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e75419ed",
   "metadata": {},
   "source": [
    "## MessagePromptTemplate\n",
    "\n",
    "LangChain提供了不同类型的 MessagePromptTemplate 。最常用的是 AIMessagePromptTemplate 、 SystemMessagePromptTemplate 和 HumanMessagePromptTemplate ，它们分别创建 AI 消息、系统消息和人工消息。\n",
    "\n",
    "但是，如果聊天模型支持使用任意角色获取聊天消息，则可以使用 ChatMessagePromptTemplate ，它允许用户指定角色名称。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5cb25410",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatMessage(content='愿force与你同在', role='Jedi')"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts import ChatMessagePromptTemplate\n",
    "\n",
    "prompt = \"愿{subject}与你同在\"\n",
    "\n",
    "chat_message_prompt = ChatMessagePromptTemplate.from_template(\n",
    "    role=\"Jedi\", template=prompt\n",
    ")\n",
    "chat_message_prompt.format(subject=\"上帝\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e37cb708",
   "metadata": {},
   "source": [
    "LangChain 还提供了 MessagesPlaceholder ，它使您可以完全控制格式化期间要呈现的消息。当您不确定消息提示模板应使用什么角色或希望在格式化期间插入消息列表时，这会很有用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2c36e554",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import (\n",
    "    ChatPromptTemplate,\n",
    "    HumanMessagePromptTemplate,\n",
    "    MessagesPlaceholder,\n",
    ")\n",
    "\n",
    "human_prompt = \"用 {word_count} 字总结我们迄今为止的对话。\"\n",
    "human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)\n",
    "\n",
    "chat_prompt = ChatPromptTemplate.from_messages(\n",
    "    [MessagesPlaceholder(variable_name=\"conversation\"), human_message_template]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b5638008",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='学习编程最好的方法是什么?'),\n",
       " AIMessage(content='1. 选择编程语言：决定您想要学习的编程语言。\\n\\n2. 从基础开始：熟悉变量、数据类型和控制结构等基本编程概念。\\n\\n3. 练习、练习、再练习：学习编程的最好方法是通过实践经验'),\n",
       " HumanMessage(content='用 10 字总结我们迄今为止的对话。')]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "\n",
    "human_message = HumanMessage(content=\"学习编程最好的方法是什么?\")\n",
    "ai_message = AIMessage(\n",
    "    content=\"\"\"\\\n",
    "1. 选择编程语言：决定您想要学习的编程语言。\n",
    "\n",
    "2. 从基础开始：熟悉变量、数据类型和控制结构等基本编程概念。\n",
    "\n",
    "3. 练习、练习、再练习：学习编程的最好方法是通过实践经验\\\n",
    "\"\"\"\n",
    ")\n",
    "\n",
    "chat_prompt.format_prompt(\n",
    "    conversation=[human_message, ai_message], word_count=\"10\"\n",
    ").to_messages()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ff95fc69",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'学习编程，从基础练习。'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "openai_api_key = \"EMPTY\"\n",
    "openai_api_base = \"http://127.0.0.1:1234/v1\"\n",
    "chat = ChatOpenAI(\n",
    "    openai_api_key=openai_api_key,\n",
    "    openai_api_base=openai_api_base,\n",
    "    temperature=0.7,\n",
    ")\n",
    "\n",
    "output_parser = StrOutputParser()\n",
    "\n",
    "chain = chat_prompt | chat | output_parser\n",
    "\n",
    "chain.invoke({\"word_count\":\"10\",\"conversation\":[human_message, ai_message]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "519a95b3",
   "metadata": {},
   "source": [
    "## 部分提示模板\n",
    "\n",
    "与其他方法一样，“部分”提示模板是有意义的 - 例如传入所需值的子集，以创建一个仅需要剩余值子集的新提示模板。\n",
    "\n",
    "LangChain 通过两种方式支持这一点： \n",
    "1. 使用字符串值进行部分格式化。 \n",
    "2. 使用返回字符串值的函数进行部分格式化。\n",
    "\n",
    "这两种不同的方式支持不同的用例。在下面的示例中，我们将回顾这两个用例的动机以及如何在 LangChain 中实现这一点。\n",
    "\n",
    "\n",
    "### 部分带字符串\n",
    "\n",
    "想要部分提示模板的一个常见用例是，如果您先于其他变量获取某些变量。例如，假设您有一个提示模板需要两个变量 foo 和 baz 。如果您在链的早期获得 foo 值，但稍后获得 baz 值，那么等到两个变量都位于同一位置才能将它们传递给提示模板。相反，您可以使用 foo 值部分化提示模板，然后传递部分化的提示模板并直接使用它。下面是执行此操作的示例：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "ea7942b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "foobaz\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "prompt = PromptTemplate.from_template(\"{foo}{bar}\")\n",
    "partial_prompt = prompt.partial(foo=\"foo\")\n",
    "print(partial_prompt.format(bar=\"baz\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "256840a8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "foobaz\n"
     ]
    }
   ],
   "source": [
    "#您还可以仅使用部分变量初始化提示。\n",
    "\n",
    "prompt = PromptTemplate(\n",
    "    template=\"{foo}{bar}\", input_variables=[\"bar\"], partial_variables={\"foo\": \"foo\"}\n",
    ")\n",
    "print(prompt.format(bar=\"baz\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f2fa77c",
   "metadata": {},
   "source": [
    "## 部分函数\n",
    "\n",
    "另一个常见用途是对函数进行部分处理。这样做的用例是当您有一个变量时，您知道您总是希望以通用方式获取该变量。一个典型的例子是日期或时间。想象一下，您有一个提示，您总是希望获得当前日期。您无法在提示中对其进行硬编码，并且将其与其他输入变量一起传递有点烦人。在这种情况下，能够使用始终返回当前日期的函数来部分提示是非常方便的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "1d9ec563",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "\n",
    "\n",
    "def _get_datetime():\n",
    "    now = datetime.now()\n",
    "    return now.strftime(\"%m/%d/%Y\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e34ba0ce",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "给我讲一个关于 03/08/2024 天的 有趣 故事\n"
     ]
    }
   ],
   "source": [
    "prompt = PromptTemplate(\n",
    "    template=\"给我讲一个关于 {date} 天的 {adjective} 故事\",\n",
    "    input_variables=[\"adjective\", \"date\"],\n",
    ")\n",
    "partial_prompt = prompt.partial(date=_get_datetime)\n",
    "print(partial_prompt.format(adjective=\"有趣\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e3703af7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "给我讲一个关于 03/08/2024 天的 funny 故事\n"
     ]
    }
   ],
   "source": [
    "prompt = PromptTemplate(\n",
    "    template=\"给我讲一个关于 {date} 天的 {adjective} 故事\",\n",
    "    input_variables=[\"adjective\"],\n",
    "    partial_variables={\"date\": _get_datetime},\n",
    ")\n",
    "print(prompt.format(adjective=\"funny\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a43f09c5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
